# -*- coding: utf-8 -*-

"""
Description: 喀斯玛官网逆向接口
"""

import requests
import json
import re
from lxml import etree
import ddddocr
import os
from http.cookiejar import LWPCookieJar
import time
from functools import wraps
import aiohttp
from csm.utils import del_prolist_attrs

module_src = os.path.abspath(__file__)
module_dir = os.path.dirname(module_src)

BASE_URL = "https://supplier.casmart.com.cn"

BANKEND_DOMAIN = "https://supplier.casmart.com.cn"


class SessionManager:
    """
    会话维持类

    Args:
        session_file (string): 会话文件名
    """

    USERNAME = "1757236480@qq.com"  # 用户名
    PASSWORD = "A123456789a"  # 密码
    LOGIN_URL = "https://sso.casmart.com.cn/login?channel-id=1&return-url=http://supplier.casmart.com.cn/"  # 登录地址
    CHANNEL_ID = "1"  # 渠道ID
    RETURN_URL = "http://supplier.casmart.com.cn/"

    def __init__(self, session_file: str = "cookies.txt"):
        self.session_file_path = f"{module_dir}/{session_file}"
        self.cookies = None
        self.session = requests.Session()  # 创建共用会话
        self.session.verify = False
        # self.session.proxies = {"http": "http://127.0.0.1:7890", "https": "http://127.0.0.1:7890"}
        # 添加请求头
        self.session.headers.update(
            {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36",
                "Referer": "https://supplier.casmart.com.cn",
            }
        )
        self.load_session()

    def load_session(self):
        """
        加载持久化cookies
        """
        if os.path.exists(self.session_file_path):  # 判断是否存在 session 文件
            try:
                self.session.cookies = LWPCookieJar(filename=self.session_file_path)
                self.session.cookies.load(ignore_discard=True, ignore_expires=True)
                if self.check_login():
                    print("✅cookies 有效")
                else:
                    self.init_login()
            except Exception as e:
                print("加载 cookies 失败：", e)

        else:  # 不存在就直接重新初始化 Session 文件
            self.init_login()

    def check_login(self):
        """
        检查session登录状态

        通过访问用户资料的方式检测登录状态
        """

        DEATAIL_URL = "https://supplier.casmart.com.cn/back-apply/detail"
        resp = self.session.get(url=DEATAIL_URL)
        if resp.status_code == 200:
            return True
        return False

    def _get_verify_code(self, max_retries: int = 3) -> str:
        """获取验证码

        Args:
            max_retries (int, optional): 默认重试次数. Defaults to 3.

        Raises:
            Exception: 获取验证码图片异常

        Returns:
            str: 验证码字符串
        """
        VERYFY_URL = "https://sso.casmart.com.cn/verify-code"
        res_verify = self.session.get(url=VERYFY_URL)  # 请求验证码
        if res_verify.status_code == 200:
            ocr = ddddocr.DdddOcr(show_ad=False)
            verify_code = ocr.classification(res_verify.content)
            # print(f"VerifyCode: {verify_code}")
            return verify_code
        else:
            raise Exception(f"❌获取验证码失败")

    def _get_client_key(self, verify_code: str):  # 获取登录凭证
        """发起登录，获取 client_key

        Args:
            verify_code (str): 验证码

        Raises:
            Exception: _description_

        Returns:
            _type_: _description_
        """

        LOGIN_URL = "https://sso.casmart.com.cn/login"
        heardes = {  # 请求头
            "Origin": "https://sso.casmart.com.cn/",
            "content-type": "application/json",
        }

        user_data = {  # 登录参数
            "channelId": self.CHANNEL_ID,
            "returnUrl": "http://supplier.casmart.com.cn/",
            "userName": self.USERNAME,
            "password": self.PASSWORD,
            "verifycode": verify_code,
        }

        response = self.session.post(
            url=LOGIN_URL, headers=heardes, data=json.dumps(user_data)
        )

        if response.status_code == 200:
            print(response.json())
            print(response.cookies)
            return response.json()["data"]
        else:
            print(response.json()["data"])
            raise Exception("❌登录失败")

    def _post_redirect(self, client_key):
        """
        换取登录session

        携带`client_key`请求后,会自动跳转到`returnUrl`地址并设置`cookie`

        获取 cookie:

        acw_tc/index_user/supplier_sso_id/supplier_token/supplier_token_domain
        """
        REDIRECT_URL = "https://sso.casmart.com.cn/redirect"

        data = {
            "returnUrl": self.RETURN_URL,
            "channelId": self.CHANNEL_ID,
            "clientKey": client_key,
        }

        response = self.session.post(url=REDIRECT_URL, data=data)
        if response.status_code == 200:

            print("======= 获取 cookie =======")
            print("✅登录成功")
        else:
            raise Exception("❌获取登录 Session 失败")

    def _get_supplier_stationid(self):
        url = f"{BANKEND_DOMAIN}/index/firstSupplierStationId"
        response = self.session.get(url=url)
        if response.status_code == 200:
            print("✅获取 firstSupplierStationId 成功")
        else:
            raise Exception("❌获取 firstSupplierStationId 失败")

    def init_login(self):
        """
        初始化登录
        """
        max_retries = 5
        self.session.cookies = LWPCookieJar(filename=self.session_file_path)
        for attempt in range(1, max_retries + 1):
            try:
                # 1.获取验证码
                verify_code = self._get_verify_code()
                # 2.获取登录凭证
                key = self._get_client_key(verify_code)
                # 3.获取登录 cookie
                self._post_redirect(key)

                # 4.获取 firstSupplierStationId
                self._get_supplier_stationid()
                # 持久化所有 cookies
                self.session.cookies.save(ignore_discard=True, ignore_expires=True)
                print("✅cookies保存成功")
                # 登录成功，终止循环
                break

            except Exception as e:
                print(f"第{attempt}次尝试失败：{e}")
                if attempt == max_retries:
                    print("已达到最大重试次数，操作失败。")
                else:
                    time.sleep(1)  # 等待1秒后重试


class RequestHanlder(SessionManager):  # 请求类 继承于`SessionManager`
    """
    请求类 继承于`SessionManager`
    """

    def __init__(self, session_file="cookies.txt"):
        super().__init__(session_file)
        self._sem = 3  # 并发数量
        # headers = {
        #     "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36",
        #     "Referer": "https://supplier.casmart.com.cn/",
        # }
        # self.async_session = aiohttp.ClientSession(
        #     headers=headers, cookies=self.cookies
        # )

    def get_product_list(self, data: dict = {}) -> dict:
        """
        [商家后台] 获取商品列表

        tabIndex 搜索类型 0全部 1上架 2下架 3系统下架 4未审核 5待补充 6审核不通过
        """
        SEARCH_URL = f"{BANKEND_DOMAIN}/product/basic/list"

        json_data = {
            "page": 1,
            "count": 50,
            "params": {
                "brandName": "",
                "cateId": "",
                "tabIndex": "0",
                "stationId": "",
                "brandId": "",
                "deliveryId": "",
                "searchType": "0",
                "search": "",
            },
        }
        json_data.update(data)

        resp = self.session.post(url=SEARCH_URL, json=json_data)
        return resp.json()

    def del_product(self, ids: list) -> dict:
        """[商家后台] 删除指定商品列表

        Args:
            ids (list): 商品id 列表

        Returns:
            dict: _description_
        """
        url = f"{BANKEND_DOMAIN}/product/basic/updateStatus"
        json_data = {"ids": ",".join(str(item) for item in ids), "trash": 1, "type": 2}
        resp = self.session.post(url=url, json=json_data)
        return resp.json()

    def get_brand_list(self):
        """[商家后台] 获取品牌列表"""
        url = f"{BANKEND_DOMAIN}/product/agent/listBrandBySupplierId"
        resp = self.session.get(url=url)
        return resp.json()

    def get_cate_list(self):
        """[商家后台] 获取类目列表"""
        url = f"{BANKEND_DOMAIN}/api/category/tree/valid"
        resp = self.session.get(url=url)
        return resp.json()["data"]

    def get_product_extend(self, type_id: int):
        """[商家后台] 获取商品扩展信息

        Args:
            type_id (int): 商品类型id
        """
        url = f"{BANKEND_DOMAIN}/product/basic/getProductExtendByTypeId"
        params = {"typeId": type_id}
        resp = self.session.get(url=url, params=params)
        return resp.json()["data"]

    def get_cate_extend(self, cate_id: int):
        """[商家后台] 获取分类扩展信息

        Args:
            cate_id (int): 分类id

        Returns:
            _type_: _description_
        """
        url = f"{BANKEND_DOMAIN}/product/basic/getTypeAndExtendByCateId"
        params = {"cateId": cate_id}
        resp = self.session.get(url=url, params=params)
        return resp.json()["data"]

    def get_cate_unit(self, cate_id: int):
        """[商家后台] 获取分类单位

        Args:
            cate_id (int): 分类id

        Returns:
            _type_: _description_
        """
        url = f"{BANKEND_DOMAIN}/product/basic/listUnitInfoByCateId"
        params = {"cateId": cate_id}
        resp = self.session.get(url=url, params=params)
        return resp.json()["data"]

    def upload_product(self, pro_data: dict):
        """[商家后台页面] 上传商品"""
        url = f"{BANKEND_DOMAIN}/product/basic/save"
        data = json.dumps(pro_data, separators=(",", ":"))
        headers = {
            "Referer": "https://supplier.casmart.com.cn/product/basic/add",
            "Content-Type": "application/json",
        }
        self.session.headers.update(headers)
        resp = self.session.post(url=url, data=data)
        return resp

    def updata_product(self, pro_data: dict):
        """[商家后台] 更新商品信息

        Args:
            pro_data (dict): 商品信息
        """
        url = f"{BANKEND_DOMAIN}/product/basic/update"
        data = json.dumps(pro_data, separators=(",", ":"))
        headers = {
            "Referer": "https://supplier.casmart.com.cn/product/basic/list",
            "Content-Type": "application/json",
        }
        self.session.headers.update(headers)
        resp = self.session.post(url=url, data=data)
        return resp

    def get_product_count(self):
        url = f"{BANKEND_DOMAIN}/product/basic/getProductCount"
        resp = self.session.get(url=url)
        return resp.json()

    def search_store_prolist(
        self,
        domain: str,
        page_num: int,
        query_string: str = "",
        order_by: str = "hotsale_down",
    ) -> list:
        """[买家前端] 商家店铺商品列表搜索

        domain 示例: a300061064

        Args:
            domain (str): 商家店铺 domain (需在买家前端获取)
            page_num (int): 页码
            query_string (str): 搜索关键词 默认为空
            order_by (str): 销量排序 默认降序
        Raises:
            Exception: _description_

        Returns:
            list: _description_
        """
        SEARCH_URL = f"https://{domain}.casmart.com.cn/shop/products"
        headers = {
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "Content-Type": "application/json",
            "Origin": domain,
            "Pragma": "no-cache",
            "Referer": SEARCH_URL,
        }
        self.session.headers.update(headers)

        max_retries = 5
        retry_delay = 1

        # 搜索参数
        search_data = {
            "queryString": query_string,
            "queryType": "pro",
            "minprice": "",
            "maxprice": "",
            "supplierCateId": "-1",
        }

        data = {
            "initPage": True,
            "pageNum": page_num,
            "pageSize": 20,
            "orderBy": order_by,
            "supplierCateId": "-1",
            "queryString": query_string,
            "minPrice": None,
            "maxPrice": None,
            "queryType": "pro",
        }
        data = json.dumps(data, separators=(",", ":"))
        for attempt in range(max_retries):
            resp = self.session.post(url=SEARCH_URL, data=data, params=search_data)

            # 请求状态码处理
            if resp.status_code == 200:
                return del_prolist_attrs(resp.json()["dataList"])
            else:
                print(f"获取数据失败，{attempt+1}/{max_retries}次尝试")
                if attempt < max_retries - 1:
                    time.sleep(retry_delay)

        raise Exception(f"{page_num}页获取失败❌")

    def get_store_prodes(self, supplier_id: int, pro_id: int):
        """[买家前端] 获取店铺商品详情信息

        Args:
            supplier_id (int): 店铺ID
            pro_id (int): 产品ID

        Raises:
            Exception: _description_

        Returns:
            _type_: _description_
        """
        deatil_url = (
            f"https://www.casmart.com.cn/product-details/page/{supplier_id}/{pro_id}"
        )
        resp = self.session.get(url=deatil_url)
        if resp.status_code == 200:
            return resp.text
        else:
            raise Exception(f"获取商品详情失败❌")

    def search_market_product(self, search_data: dict = {}):
        """
        [买家前端]商城搜索商品
        """
        SEARCH_URL = "https://www.casmart.com.cn/n/search"
        headers = {
            "referer": "https://www.casmart.com.cn/n/search",
            "host": "www.casmart.com.cn",
        }
        query_data = {"q": "安耐吉", "s": 300088574, "page": 1, "sort": 1}
        query_data.update(search_data)
        json_data = json.dumps(query_data, separators=(",", ":"))
        resp = self.session.get(url=SEARCH_URL, params=query_data, headers=headers)
        if resp.status_code == 200:
            return resp.text
        else:
            raise Exception(f"搜索商品失败❌")


class AsyncSessionManager:
    """
    异步 Session 实例
    """

    def __init__(self, session_file: str = "cookies.txt") -> None:

        # 初始化 session
        self.session = aiohttp.ClientSession(
            connector=aiohttp.TCPConnector(limit=10),
            timeout=aiohttp.ClientTimeout(total=10),
            headers={
                "accept": "application/json",
                "accept-language": "zh-CN",
                "cache-control": "no-cache",
                "origin": "https://supplier.casmart.com.cn",
                "referer": "https://supplier.casmart.com.cn",
                "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
            },
        )

        self.session_file_path = f"{module_dir}/{session_file}"
        self.session.cookies = LWPCookieJar(filename=self.session_file_path)
        self.session.cookies.load(ignore_discard=True, ignore_expires=True)

    async def close(self):
        """销毁 session 实例"""
        await self.session.close()


class AsyncRequestHanlder(AsyncSessionManager):
    """
    异步请求类 继承于`AsyncSessionManager`
    """

    def __init__(self, session_file="cookies.txt"):
        super().__init__(session_file)

    async def check_login(self):
        DEATAIL_URL = "https://supplier.casmart.com.cn/back-apply/detail"
        async with self.session.get(url=DEATAIL_URL) as resp:
            if resp.status == 200:
                print("✅cookies 有效")
            else:
                print("❌cookies 无效")

    async def upload_product(self, pro_data: dict):
        """[商家后台] 上传商品

        Args:
            pro_data (dict): 商品数据

        Returns:
            _type_: _description_
        """
        url = f"{BANKEND_DOMAIN}/product/basic/save"
        data = json.dumps(pro_data, separators=(",", ":"))
        headers = {
            "Referer": "https://supplier.casmart.com.cn/product/basic/add",
            "Content-Type": "application/json",
        }
        self.session.headers.update(headers)
        async with self.session.post(url=url, data=data) as resp:
            return await resp.json()


# A123456789a